package au.edu.qut.bpm.genbinder.genetic.crossover;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import org.uncommons.maths.number.ConstantGenerator;
import org.uncommons.maths.number.NumberGenerator;
import org.uncommons.watchmaker.framework.operators.AbstractCrossover;

import au.edu.qut.bpm.genbinder.genetic.other.LogRepairer;
import au.edu.qut.bpm.genbinder.models.Activity;
import au.edu.qut.bpm.genbinder.models.ConcreteEventLog;

public class LogActivitiesCrossoverUnsafeRepaired extends AbstractCrossover<ConcreteEventLog> {

	public LogActivitiesCrossoverUnsafeRepaired() {
		this(2);
	}

	public LogActivitiesCrossoverUnsafeRepaired(int crossoverPoints) {
		this(new ConstantGenerator<Integer>(crossoverPoints));
	}

	public LogActivitiesCrossoverUnsafeRepaired(NumberGenerator<Integer> crossoverPointsVariable) {
		super(crossoverPointsVariable);
	}

	@Override
	protected List<ConcreteEventLog> mate(ConcreteEventLog parent1, ConcreteEventLog parent2, int numberOfCrossoverPoints, Random rng) {
		ConcreteEventLog offspring1 = parent1.clone();
		ConcreteEventLog offspring2 = parent2.clone();
		
		try {
			List<Activity> activities = new ArrayList<Activity>(offspring1.getAbstractLog().getActivities());
			
			for (int ncr = 0; ncr < numberOfCrossoverPoints; ncr++) {
				int crossoverpoint = rng.nextInt(activities.size() - 1) + 1;
				for (int i = 0; i < crossoverpoint; i++) {
					copyBinding(offspring2, offspring1, activities.get(i));
					copyBinding(offspring1, offspring2, activities.get(i));
				}
			}
		} catch (Exception e1) {
			e1.printStackTrace();
		}
		
		List<ConcreteEventLog> result = new ArrayList<ConcreteEventLog>(2);
		result.add(offspring1);
		result.add(offspring2);
		
		if (!offspring1.isLogSafe() || !offspring2.isLogSafe()) {
			LogRepairer repairer = new LogRepairer();
			result = repairer.apply(result, new Random());
			return result;
		}
		return result;
	}

	private void copyBinding(ConcreteEventLog from, ConcreteEventLog to, Activity activity) {
		to.setActivityResources(activity, from.getActivityResources(activity));
		to.setActivityStart(activity, from.getActivityStart(activity));
	}
}
